/**
 * 
 */
package cn.bigcontainer.alt.meta.service.impl;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.Resource;

import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import cn.bigcontainer.alt.common.entity.BigMap;
import cn.bigcontainer.alt.common.entity.WoPage;
import cn.bigcontainer.alt.common.util.WoKit;
import cn.bigcontainer.alt.common.util.WoVelocityKit;
import cn.bigcontainer.alt.meta.dao.MDao;
import cn.bigcontainer.alt.meta.dto.MObjectDto;
import cn.bigcontainer.alt.meta.dto.MViewDto;
import cn.bigcontainer.alt.meta.exception.MException;
import cn.bigcontainer.alt.meta.extension.MListDataFilter;
import cn.bigcontainer.alt.meta.po.MProperty;
import cn.bigcontainer.alt.meta.po.MRelation;
import cn.bigcontainer.alt.meta.po.MViewProperty;
import cn.bigcontainer.alt.meta.service.MAppService;
import lombok.extern.slf4j.Slf4j;

/**
 * @author cailei
 * @date 2017年7月14日
 */
@Service
@Transactional(rollbackFor = { Exception.class })
@Primary
@Slf4j
public class MAppServiceImpl implements MAppService {

	@Resource
	private MDao woDao;

	private void getSqlFromJoin(StringBuffer sql, MObjectDto obj, Set<MRelation> relations) {
		// 3.获取from：from WO_APPS as LX_WO_APPS
		sql.append(" from " + obj.getId() + " as " + obj.getNo());
		// 4.获取left join：left join MProperty as XXX on (APPS.Id =
		// XXX.W_OBJECTS_ID and ... )
		for (MRelation r : relations) {
			if (r.is12M() || r.isM2M() || r.is2C()) {
				continue;
			}
			sql.append(" left join " + r.getRelatedObjectId() + " as " + r.getId());
			sql.append(" on (");
			List<MProperty> props = MObjectDto.getProperties(obj, r.getPropertyIds());
			List<MProperty> relatedProps = MObjectDto.getProperties(r.getRelatedObject(), r.getRelatedIds());
			for (int i = 0; i < props.size(); i++) {
				if (i > 0) {
					sql.append(" and ");
				}
				MProperty prop = props.get(i);
				MProperty relatedProp = relatedProps.get(i);
				sql.append(String.format("%s.%s = %s.%s", obj.getNo(), prop.getName(), r.getId(),
						relatedProp.getName()));
			}
			sql.append(")");
		}
	}

	/**
	 * 将数据库查询回来的多条，转化为单条记录，值以逗号隔开
	 * 
	 * @param relatedData
	 * @return
	 */
	private static Map<String, Object> getUIValues(List<Map<String, Object>> relatedData) {
		Map<String, Object> finalData = new HashMap<String, Object>();
		for (Map<String, Object> rd : relatedData) {
			for (String k : rd.keySet()) {
				Object rValNew = rd.get(k);
				if (WoKit.isEmpty(rValNew)) {
					rValNew = "";
				}
				Object rVals = finalData.get(k);
				if (rVals == null) {
					rVals = rValNew;
				} else {
					rVals += "," + rValNew;
				}
				finalData.put(k, rVals);
			}
		}
		return finalData;
	}

	/**
	 * @param listData
	 * @param list
	 * @param relationsM2M
	 */
	private void setM2MUIValues(List<Map<String, Object>> listData, MViewDto list,
                                Map<MRelation, List<MViewProperty>> relationsM2M) {
		for (MRelation r : relationsM2M.keySet()) {
			List<MViewProperty> lps = relationsM2M.get(r);
			// 关联本方对象属性
			List<MProperty> rProps = MObjectDto.getProperties(r.getObject(), r.getPropertyIds());
			// 关联他方对象属性
			List<MProperty> rrProps = MObjectDto.getProperties(r.getRelatedObject(), r.getRelatedIds());
			// 关系对象关联本方对象属性
			List<MProperty> bProps = MObjectDto.getProperties(r.getRelationObject(), r.getRelationIds());
			// 关系对象关联他方对象属性
			List<MProperty> brProps = MObjectDto.getProperties(r.getRelationObject(), r.getRelationInverseIds());
			String selectId = "select ";
			for (int i = 0; i < brProps.size(); i++) {
				if (i > 0) {
					selectId += ",";
				}
				selectId += brProps.get(i).getName();
			}
			selectId += " from " + r.getRelationObjectId() + " where (";
			for (int i = 0; i < bProps.size(); i++) {
				if (i > 0) {
					selectId += ",";
				}
				selectId += bProps.get(i).getName();
			}
			selectId += ") = (%s)";
			String sql = "select ";
			for (int i = 0; i < lps.size(); i++) {
				MViewProperty lp = lps.get(i);
				if (i > 0) {
					sql += ",";
				}
				MProperty property = list.getApp().getObject().getPropertyIncludeRelatedObjects(lp.getPropertyId());
				sql += property.getName() + " as " + lp.getId();
			}
			sql += " from " + r.getRelatedObjectId() + " where (";
			for (int i = 0; i < rrProps.size(); i++) {
				if (i > 0) {
					sql += ",";
				}
				sql += rrProps.get(i).getName();
			}
			sql += ") in (" + selectId + ")";
			for (Map<String, Object> row : listData) {
				String vals = "";
				for (int i = 0; i < rProps.size(); i++) {
					MProperty p = rProps.get(i);
					MViewProperty lp = list.getViewPropertyByPropertyId(p.getId());
					if (i > 0) {
						vals += ",";
					}
					vals += p.getSqlValue(row.get(lp.getId()));
				}
				String s = String.format(sql, vals);
				List<Map<String, Object>> relatedData = woDao.queryList(s);
				row.putAll(getUIValues(relatedData));
			}
		}
	}

	/**
	 * @param orderLPs
	 * @return
	 */
	private String getSqlOrder(List<MViewProperty> orderLPs) {
		Collections.sort(orderLPs, new Comparator<MViewProperty>() {
			@Override
			public int compare(MViewProperty p0, MViewProperty p1) {
				return p0.getOrderIndex() - p1.getOrderIndex();
			}
		});
		String sql = "";
		for (MViewProperty lp : orderLPs) {
			if (!sql.equals("")) {
				sql += ",";
			}
			sql += lp.getData().getString("mosOrderField");
		}
		if (!sql.equals("")) {
			sql = " order by " + sql;
		}
		return sql;
	}

	private boolean addWhere(StringBuffer where, MListDataFilter filter, MViewDto list, MViewDto search) {
		String filterWhere = null;
		boolean filterReplace = false;
		if (filter != null) {
			filterWhere = filter.addWhere(list, search);
			filterReplace = filter.replace();
		}
		if (!WoKit.isEmpty(filterWhere)) {
			log.info("addWhere - " + filterWhere);
			where.append(" and (");
			where.append(filterWhere);
			where.append(")");
		}
		return filterReplace;
	}

	/**
	 * @param list
	 * @param search
	 */
	public void loadListData(MViewDto list, MViewDto search) {
		MObjectDto obj = list.getApp().getObject();
		BigMap<String,Object> req = obj.getRequest();
		StringBuffer sql = new StringBuffer();
		// 1.获取字段：select APPS.Id as LX_WO_APPS_Id, ...
		sql.append("select ");

		// 本次查询中涉及到的关系有四个来源：
		// 1) 列表（list）属性关联的关系
		// 2) params中涉及的属性如果不是本对象属性，其关联的关系（暂不支持）
		// 3) params中涉及的关系
		// 4) params中涉及的查询视图（search）属性关联的关系
		Set<MRelation> relations = new HashSet<MRelation>();
		Map<MRelation, List<MViewProperty>> relationsM2M = new HashMap<MRelation, List<MViewProperty>>();
		List<MViewProperty> orderLPs = new ArrayList<MViewProperty>();
		for (int i = 0; i < list.getProperties().size(); i++) {
			MViewProperty listProp = list.getProperties().get(i);
			if (i > 0) {
				sql.append(",");
			}
			if (!WoKit.isEmpty(listProp.getExpression())) {
				sql.append(String.format("%s as %s", listProp.getExpression(), listProp.getId()));
				continue;
			}
			MProperty prop = listProp.getPropertyIncludeRelatedObjects();
			MRelation r = listProp.getRelation();
			if (listProp.getOrderIndex() > -1) {
				if (r == null || r.isM21()) {
					orderLPs.add(listProp);
				}
			}
			// 1) 列表（list）属性关联的关系
			if (r != null) {
				relations.add(r);
				if (r.isM2M()) {
					List<MViewProperty> lps = relationsM2M.get(r);
					if (lps == null) {
						lps = new ArrayList<MViewProperty>();
						relationsM2M.put(r, lps);
					}
					lps.add(listProp);
				}
			}
			if (prop == null || (r != null && (r.isM2M() || r.is12M()))) {
				// 去掉逗号
				sql.deleteCharAt(sql.length() - 1);
			} else {
				// 表别名，如果prop是本对象属性，则为本对象No，否则为关系r的Id
				String alias = null;
				if (r != null) {
					alias = r.getId();
				} else {
					alias = obj.getNo();
				}
				sql.append(String.format("%s.%s as %s", alias, prop.getName(), listProp.getId()));
				listProp.getData().put("mosOrderField",
						alias + "." + prop.getName() + " " + listProp.getOrderType());
			}
		}
		// 如果没有设置排序字段，且编号属性存在，则以编号属性排序
		if (orderLPs.size() == 0) {
			MViewProperty noProp = list.getNoProperty();
			if (noProp != null) {
				orderLPs.add(noProp);
			}
		}
		// 2.获取where（以and开头）：and xxx=yyy and (xxx=yyy or aaa=bbb)
		StringBuffer where = new StringBuffer();
		// 2.1 从params中获取MViewProperty的值
		for (MViewProperty sp : search.getProperties()) {
			Object val = req.get(sp.getId());
			if (WoKit.isEmpty(val)) {
				continue;
			}
			where.append(" and ");
			where.append(sp.getSqlCondition(val.toString()));
			MRelation r = sp.getRelation();
			if (r != null) {
				relations.add(r);
			}
		}
		// 2.2 从params中获取本方对象MProperty的值
		for (MProperty p : obj.getProperties()) {
			Object val = req.get(p.getId());
			if (WoKit.isEmpty(val)) {
				continue;
			}
			String exp = "${field} = " + p.getSqlValue("${value}");
			where.append(" and ");
			where.append(MViewProperty.getSqlCondition(obj.getNo() + "." + p.getName(), val.toString(), exp));
		}
		// 2.3 从params中获取本方对象MRelation的值
		for (MRelation r : obj.getRelations()) {
			Object val = req.get(r.getId());
			if (WoKit.isEmpty(val)) {
				continue;
			}
			where.append(" and ");
			where.append(r.getSqlCondition(val.toString()));
		}
		// 2.4 从系统配置中获取默认扩展where
		MListDataFilter filter = list.getListDataFilter();
		if (!addWhere(where, filter, list, search)) {
			filter = list.getApp().getListDataFilter();
			if (!addWhere(where, filter, list, search)) {
				filter = obj.getListDataFilter();
				addWhere(where, filter, list, search);
			}
		}
		// 3.获取from：from WO_APPS as LX_WO_APPS
		this.getSqlFromJoin(sql, obj, relations);
		// 4.拼接where：
		sql.append(" where 1=1 " + where);
		// 5.拼接order by
		sql.append(this.getSqlOrder(orderLPs));
		// 设置list视图的woData
        WoPage<Map<String, Object>> pageData = new WoPage<Map<String, Object>>();
        List<Map<String, Object>> listData = null;
        if (list.paging()) {
			int page = req.getInt("page", 1);
			int size = req.getInt("pageSize", list.getPageSize().intValue());
			listData = woDao.queryPagingList(sql.toString(), (page - 1) * size, size);
            Integer total = woDao.queryCount(sql.toString());
            pageData.setRows(listData);
            pageData.setResults(Long.valueOf(total));
            pageData.setPageSize(Long.valueOf(size));
            pageData.setPage(Long.valueOf(page));
		} else {
			listData = woDao.queryList(sql.toString());
            pageData.setRows(listData);
            pageData.setResults(Long.valueOf(listData.size()));
		}
		list.setListData(pageData);
		// 6.设置对应M2M关系的视图属性值
		setM2MUIValues(listData, list, relationsM2M);
	}

	/**
	 * @param form
	 * @param keyVal
	 */
	public void loadCreateForm(MViewDto form, String keyVal) {
		if (WoKit.isEmpty(keyVal)) {
			form.createKeyValues();
			// 加载除了主键属性之外的视图属性值
			loadForm(form, null);
		} else {
			loadUpdateForm(form, keyVal);
			form.createKeyValues();
		}
	}

	@Resource
	private WoVelocityKit woVelocityKit;

	/**
	 * 加载form中除了主键属性之外的所有属性值
	 * 
	 * @param form
	 * @param formData
	 */
	private void loadForm(MViewDto form, Map<String, Object> formData) {
		MObjectDto obj = form.getApp().getObject();
		Map<String,Object> req = obj.getRequest();
		Map<String, Object> data = new HashMap<String, Object>();
		if (req != null) {
			data.putAll(req);
		}
		if (formData != null) {
			data.putAll(formData);
		}
		// MViewProperty noFp = null;
		// String noValue = null;
		for (MViewProperty fp : form.getProperties()) {
			MProperty p = fp.getPropertyIncludeRelatedObjects();
			Object val = null;
			if (p != null) {
				if (p.getKey() == 1) {
					continue;
				}
				// if (p.getName().equalsIgnoreCase(obj.getNoPropName())) {
				// noFp = fp;
				// }
				// 从request中获取属性值
				val = data.get(p.getId());
			} else {
				String rId = fp.getRelationId();
				if (!WoKit.isEmpty(rId)) {
					val = data.get(rId);
					if (WoKit.isEmpty(val)) {
						MRelation relation = obj.getRelation(rId);
						val = data.get(relation.getPropertyIds());
					}
					// 如果是创建视图，并且配置了编号属性，则自动创建编号：上级编号+最大下级编号
					// if (!WoKit.isEmpty(val) && formData == null &&
					// obj.hasNoProp()) {
					// MRelation pr = obj.getParentRelation();
					// if (pr == null) {
					// throw new MException(MException.NO_PARENT,
					// obj.getId());
					// }
					// if (pr.getId().equals(rId)) {
					// noValue = obj.createNextChildNoValue(val.toString());
					// }
					// }
				}
			}
			if (!WoKit.isEmpty(val)) {
				fp.setValue(val);
				// fp.setReadOnly(1);
				// loadRelatedNameValue (fp, val);
				continue;
			}
			// 从W_INITVALUE中获取值
			val = data.get(fp.getId());
			if (fp.isReplaceInit(val)) {
				val = woVelocityKit.parse(fp.getInitValue(), data);
			}
			fp.setValue(val);
			// loadRelatedNameValue (fp, val);
		}
		// if (noFp != null && WoKit.isEmpty(noFp.getValue()) &&
		// !WoKit.isEmpty(noValue)) {
		// noFp.setValue(noValue);
		// }
	}

	/**
	 * @param form 视图
	 * @param fp 视图属性
	 * @param create 是否新建视图
	 */
	private void updateBridgeData(MViewDto form, MViewProperty fp, String vals[], boolean create) {
		MObjectDto obj = form.getApp().getObject();
		BigMap<String,Object> req = obj.getRequest();
		MRelation r = fp.getRelation();
		List<MProperty> rProps = r.getProperties();
		List<MProperty> brProps = r.getRelationInverseProperties();
		List<MProperty> bProps = r.getRelationProperties();
		String[] myVals = new String[rProps.size()];
		// 删除已有关系数据
		String delSql = "delete from " + r.getRelationObjectId() + " where ";
		for (int i = 0; i < myVals.length; i++) {
			MProperty rP = rProps.get(i);
			MViewProperty f = form.getViewPropertyByPropertyId(rP.getId());
			if (f == null) {
				throw new MException(MException.VIEW_P, rP.getId());
			}
			myVals[i] = req.getString(f.getId());
			if (WoKit.isEmpty(myVals[i])) {
				throw new MException(MException.VIEW_VALUE, f.getId());
			}
			if (i > 0) {
				delSql += " and ";
			}
			MProperty bP = bProps.get(i);
			delSql += bP.getName() + "=" + bP.getSqlValue(myVals[i]);
		}
		if (!create) {
			woDao.delete(delSql);
		}
		int brCount = brProps.size();
		if (vals.length % brCount != 0) {
			throw new MException(MException.VIEW_RV, fp.getId());
		}
		int rCount = vals.length / brCount;
		// 插入关系数据
		String bSql = "insert into " + r.getRelationObjectId() + " (";
		for (int i = 0; i < bProps.size(); i++) {
			if (i > 0) {
				bSql += ",";
			}
			MProperty bP = bProps.get(i);
			bSql += bP.getName();
		}
		for (int i = 0; i < brCount; i++) {
			MProperty bP = brProps.get(i);
			bSql += "," + bP.getName();
		}
		if (r.isM2CM()) {
			bSql += "," + r.getRelationRelatedObjectIdProperty().getName();
		}
		bSql += ") values (";
		for (int i = 0; i < rCount; i++) {
			String s = bSql;
			for (int j = 0; j < myVals.length; j++) {
				if (j > 0) {
					s += ",";
				}
				s += bProps.get(j).getSqlValue(myVals[j]);
			}
			for (int j = 0; j < brCount; j++) {
				// 如果包含表名，则vals中的元素格式为：sys_user:1（表名：sys_user，值：为1）；否则为：1
				String[] tableVal = vals[i * brCount + j].split("[:]");
				if (r.isM2CM() && tableVal.length != 2) {
					throw new MException(MException.RELATION_VAL_FMT);
				}
				s += "," + brProps.get(j).getSqlValue(tableVal[tableVal.length - 1]);
				// 插入vals的数值中的表名
				if (r.isM2CM() && j == (brCount - 1)) {
					s += ", '" + tableVal[0] + "'";
				}
			}
			s += ")";
			woDao.insert(s);
		}
	}

	/**
	 * @param fps
	 * @param uniques
	 * @return -1表示完全不包含唯一键；0表示包含部分唯一键；1表示包含所有唯一键
	 */
	private int setUniquesValue(List<MViewProperty> fps, List<MProperty[]> uniques) {
		if (uniques.size() == 0) {
			throw new MException(MException.UK, fps.get(0).getView().getApp().getObject().getName());
		}
		int total = 0;
		int valCount = 0;
		for (MProperty[] arr : uniques) {
			for (int i = 0; i < arr.length; i++) {
				MProperty unique = arr[i];
				total++;
				for (MViewProperty fp : fps) {
					MProperty p = fp.getPropertyIncludeRelatedObjects();
					if (p != null && p.getId().equals(unique.getId())) {
						Object val = fp.getValue();
						if (WoKit.isEmpty(val)) {
							throw new MException(MException.VIEW_VALUE, fp.getName());
						}
						unique.setValue(val);
						valCount++;
						continue;
					}
				}
			}
		}
		if (valCount != 0 && valCount == total) {
			return 1;
		}
		if (valCount == 0) {
			return -1;
		}
		return 0;
	}

	/**
	 * @param data
	 * @param uniques
	 *            不能为空或者0长度列表
	 * @return
	 */
	private void setUniquesValue(Map<String, Object> data, List<MProperty[]> uniques) {
		for (MProperty[] arr : uniques) {
			for (int i = 0; i < arr.length; i++) {
				MProperty unique = arr[i];
				if (unique.getValue() != null) {
					continue;
				}
				Object val = data.get(unique.getName());
				if (WoKit.isEmpty(val)) {
					throw new MException(MException.PROP_VALUE, unique.getName());
				}
				unique.setValue(val);
			}
		}
	}

	private void insertM21RelationValues(MViewProperty fpRelation, MRelation r) {
		List<MProperty> rProps = r.getProperties();
		Object val = fpRelation.getValue();
		if (WoKit.isEmpty(val)) {
			log.info("insertM21RelationValues - value isEmpty");
			return;
		}
		String vals[] = new String[0];
		if (!WoKit.isEmpty(val)) {
			vals = val.toString().split(",");
		}
		if (vals.length != rProps.size()) {
			throw new MException(MException.VIEW_RV, fpRelation.getId());
		}
		String sqlFields = "";
		String sqlValues = "";
		for (int i = 0; i < rProps.size(); i++) {
			MProperty rP = rProps.get(i);
			sqlFields += "," + rP.getName();
			sqlValues += "," + rP.getSqlValue(vals[i]);
		}
		r.getData().put("woSqlFields", sqlFields);
		r.getData().put("woSqlValues", sqlValues);
	}

	private void createKeyValue(MProperty p) {
		if (MProperty.TYPE_VARCHAR.equals(p.getType())) {
			p.setValue(WoKit.generateKey32());
		} else if (MProperty.TYPE_INTEGER.equals(p.getType()) || MProperty.TYPE_SMALLINT.equals(p.getType())) {
			Object val = woDao.queryMax(p.getObject().getId(), p.getName());
			if (val == null) {
				val = 0;
			} else {
				val = ((Integer) val) + 1;
			}
			p.setValue(val);
		} else {
			throw new MException(MException.PK_TYPE, p.getId());
		}
	}

	private void createKeyValues(List<MProperty> props) {
		for (MProperty p : props) {
			if (!WoKit.isEmpty(p.getValue())) {
				continue;
			}
			createKeyValue(p);
		}
	}

	private void setKeyValues(List<MProperty> props, MViewDto form) {
		for (MProperty p : props) {
			MViewProperty fp = form.getViewPropertyByPropertyId(p.getId());
			if (fp != null) {
				if (WoKit.isEmpty(fp.getValue())) {
					throw new MException(MException.VIEW_VALUE, fp.getName());
				}
				p.setValue(fp.getValue());
			}
		}
	}

	/**
	 * 插入时，验证唯一性约束
	 * 
	 * @param uniques
	 *            对象obj的所有唯一约束组合
	 * @param obj
	 *            对象
	 */
	private void validateUniques(List<MProperty[]> uniques, MObjectDto obj, String keyValues) {
		for (MProperty[] uArray : uniques) {
			String sql = "select count(*) from " + obj.getId() + " where ";
			for (int i = 0; i < uArray.length; i++) {
				if (i > 0) {
					sql += " and ";
				}
				sql += uArray[i].getName() + "=" + uArray[i].getSqlValue();
			}
			if (!WoKit.isEmpty(keyValues)) {
				sql += " and not(" + obj.getKeysEqualCondition(false, keyValues) + ")";
			}
			if (woDao.queryInteger(sql) > 0) {
				throw new MException(MException.UK_INSERT_CONSTRAINTS, obj.getName());
			}
		}
	}

	/**
	 * submitCreateForm时，提交的M21关联对象的属性值，作为insert处理
	 * 
	 * @param relations
	 */
	private void insertRelatedProps(Set<MRelation> relations, MViewDto form) {
		for (MRelation r : relations) {
			if (r.isM21()) {
				List<MViewProperty> fps = (List<MViewProperty>) r.getData().get("woFormProp");
				MViewProperty fpRelation = (MViewProperty) r.getData().get("woFormPropRelation");
				if (fps == null) {
					if (fpRelation == null) {
						log.warn("impossible ...");
						continue;
					}
					this.insertM21RelationValues(fpRelation, r);
				} else {
					List<MProperty[]> uniques = r.getRelatedObject().getUniqueProps();
					List<MProperty> keys = r.getRelatedObject().getKeyProperties();
					// 关联对象无唯一键
					if (uniques.size() == 0) {
					} else {
						int contains = setUniquesValue(fps, uniques);
						// fps 未包含全部唯一键，则抛异常
						if (contains != 1) {
							throw new MException(MException.UK_INSERT_CONTAINS,
									r.getRelatedObject().getName());
						}
						// 验证插入时是否会违反唯一性约束
						validateUniques(uniques, r.getRelatedObject(), null);
					}
					// 设置主键值
					setKeyValues(keys, form);
					// 如果主键值未设置，则创建主键值
					createKeyValues(keys);
					// 插入关联对象表sql
					String sql = "insert into " + r.getRelatedObjectId() + " (";
					// 插入关联对象表的值
					String values = "";
					// 处理主键的值插入
					for (int i = 0; i < keys.size(); i++) {
						MProperty k = keys.get(i);
						if (!values.equals("")) {
							sql += ",";
							values += ",";
						}
						sql += k.getName();
						values += k.getSqlValue(k.getValue());
					}
					// 处理表单属性的值插入
					for (MViewProperty fp : fps) {
						if (!values.equals("")) {
							sql += ",";
							values += ",";
						}
						MProperty p = fp.getPropertyIncludeRelatedObjects();
						sql += p.getName();
						values += p.getSqlValue(fp.getValue());
					}
					sql += ") values (" + values + ")";
					woDao.insert(sql);
					// 更新本对象表的外键值
					String sqlFields = "";
					String sqlValues = "";
					List<MProperty> rProps = r.getProperties();
					for (int i = 0; i < rProps.size(); i++) {
						MProperty rP = rProps.get(i);
						sqlFields += "," + rP.getName();
						sqlValues += "," + rP.getSqlValue(keys.get(i).getValue());
					}
					r.getData().put("woSqlFields", sqlFields);
					r.getData().put("woSqlValues", sqlValues);
				}
			}
		}
	}

	/**
	 * @param form
	 */
	public void submitCreateForm(MViewDto form) {
		MObjectDto obj = form.getApp().getObject();
		BigMap<String,Object> req = obj.getRequest();
		// fields of insert sql: xxx,yyy,zzz
		String sqlFields = "";
		// values of insert sql: null, 1, 'xxx'
		String sqlValues = "";
		String used = "used";
		// 获取req中传来的参数值
		for (MProperty p : obj.getProperties()) {
			String val = req.getString(p.getId());
			if (WoKit.isEmpty(val)) {
				continue;
			}
			sqlFields += "," + p.getName();
			sqlValues += "," + p.getSqlValue(val.toString());
			MViewProperty fp = form.getViewPropertyByPropertyId(p.getId());
			if (fp != null) {
				p.getData().put("woUsed", used);
				req.put(fp.getId(), val);
				fp.setValue(val);
			}
		}
		Set<MRelation> relatedRelations = new HashSet<MRelation>();

		// 如果主键没有值，则为其生成
		if (form.getKeyProperties().size() > 0) {
			if (req.get(form.getKeyProperties().get(0).getId()) == null) {
				form.createKeyValues();
				for (MViewProperty keyVp : form.getKeyProperties()) {
					req.bigPut(keyVp.getId(), keyVp.getValue());
				}
			}
		}
		for (MViewProperty fp : form.getProperties()) {
			if (fp.getSubmit() == 0) {
				continue;
			}
			String val = req.getString(fp.getId());
			if (fp.isReplaceSubmit(val)) {
				val = this.woVelocityKit.parse(fp.getSubmitValue(), req);
				req.put(fp.getId(), val);
			}
			String vals[] = new String[0];
			if (!WoKit.isEmpty(val)) {
				vals = val.split(",");
			}
			fp.setValue(val);
			log.info(fp.getId() + "=" + val);
			// TODO 正确性检查
			// 获取关系
			MRelation r = fp.getRelation();
			// 从本对象获取属性
			MProperty p = null;
			if (!WoKit.isEmpty(fp.getPropertyId())) {
				p = obj.getProperty(fp.getPropertyId());
				// 本方属性存在
				if (p != null) {
					// MProperty已经处理过对应的属性，或者是编号属性，则忽略
					if (p.getData().get("woUsed") == used || p.getName().equalsIgnoreCase(obj.getNoPropName())) {
						continue;
					}
					sqlFields += "," + p.getName();
					sqlValues += "," + p.getSqlValue(val);
					continue;
				}

				// 本方属性不存在，且关系不存在
				if (r == null) {
					throw new MException(MException.VIEW_PR, fp.getId());
				}
				// 关联方对象属性，且关系为12M或者M2M
				if (r.is12M() || r.isM2M()) {
					throw new MException(MException.VIEW_PR, fp.getId());
				}
				// 获取关联方对象属性
				p = r.getRelatedObject().getProperty(fp.getPropertyId());
				// 关联方对象属性不存在，且属性存在
				if (p == null) {
					throw new MException(MException.VIEW_PR, fp.getId());
				}
				r.getData().addToList("woFormProp", fp);
				relatedRelations.add(r);
				continue;
			}
			// 属性ID为空，且关系对象为空
			if (r == null) {
				log.warn(obj.getId() + "属性和关系同时为空！");
				continue;
			}
			// 属性ID为空，关系只能是12M/M2M/M2CM
			if (r.is12M()) {
				// TODO
				if (vals.length == 0) {
					continue;
				}
			} else if (r.isM21()) {
				if (!WoKit.isEmpty(req.getString(r.getPropertyIds()))) {
					continue;
				}
				if (vals.length > 0) {
					r.getData().put("woFormPropRelation", fp);
					relatedRelations.add(r);
				}
				// 如果是创建视图，并且配置了编号属性，则自动创建编号：上级编号+最大下级编号
				if (obj.hasNoProperty()) {
					MRelation pr = obj.getParentRelation();
					if (pr == null) {
						throw new MException(MException.NO_PARENT, obj.getId());
					}
					if (pr.getId().equals(r.getId())) {
						String noValue = obj.createNextChildNoValue((String) val);
						// 插入编号属性值
						if (!WoKit.isEmpty(noValue)) {
							sqlFields += "," + obj.getNoPropName();
							sqlValues += ",'" + noValue + "'";
						}
					}
				}
				// List<MProperty> rProps = r.getProps();
				// if (vals.length != rProps.size()) {
				// throw new MException(MException.ERROR_VIEW_RV,
				// fp.getId());
				// }
				// for (int i = 0; i < rProps.size(); i++) {
				// MProperty rP = rProps.get(i);
				// sqlFields += "," + rP.getName();
				// sqlValues += "," + rP.getSqlValue(vals[i]);
				// }
			} else if (r.isM2M() || r.isM2CM()) {
				if (vals.length == 0) {
					continue;
				}
				this.updateBridgeData(form, fp, vals, true);
			} else {
				throw new MException(MException.VIEW_PR, fp.getId());
			}
		}

		// 验证唯一性
		List<MProperty[]> uniques = obj.getUniqueProps();
		if (uniques.size() > 0) {
			int contains = setUniquesValue(form.getProperties(), uniques);
			// fps 未包含全部唯一键，则抛异常
			if (contains != 1) {
				throw new MException(MException.UK_INSERT_CONTAINS, obj.getName());
			}
			// 验证插入时是否会违反唯一性约束
			validateUniques(uniques, obj, null);
		}
		// 插入关联对象表数据，从MRelation的woData中返回外键字段及值
		this.insertRelatedProps(relatedRelations, form);
		for (MRelation r : relatedRelations) {
			Object v = r.getData().get("woSqlFields");
			if (WoKit.isEmpty(v)) {
				continue;
			}
			sqlFields += v;
			sqlValues += r.getData().get("woSqlValues");
		}
		String sql = String.format("insert into %s (%s) values (%s)", obj.getId(), sqlFields.substring(1),
				sqlValues.substring(1));
		woDao.insert(sql);
	}

	private void set2MValues(MViewDto form, Map<String, Object> row, Set<MRelation> relations) {
		for (MRelation r : relations) {
			if (!r.isM2M()) {
				continue;
			}
			List<MViewProperty> fps = (List<MViewProperty>) r.getData().get("woFormProp");
			// 关联本方对象属性
			List<MProperty> rProps = r.getProperties();
			// 关联他方对象属性
			List<MProperty> rrProps = r.getRelatedProperties();
			// 关系对象关联本方对象属性
			List<MProperty> bProps = r.getRelationProperties();
			// 关系对象关联他方对象属性
			List<MProperty> brProps = r.getRelationInverseProperties();
			String selectId = "select ";
			for (int i = 0; i < brProps.size(); i++) {
				if (i > 0) {
					selectId += ",";
				}
				selectId += brProps.get(i).getName();
			}
			selectId += " from " + r.getRelationObjectId() + " where (";
			for (int i = 0; i < bProps.size(); i++) {
				if (i > 0) {
					selectId += ",";
				}
				selectId += bProps.get(i).getName();
			}
			selectId += ") = (%s)";
			String sql = "select ";
			for (int i = 0; i < fps.size(); i++) {
				MViewProperty lp = fps.get(i);
				if (i > 0) {
					sql += ",";
				}
				MProperty p = lp.getPropertyIncludeRelatedObjects();
				if (p == null) {
					sql += "concat(";
					for (int m = 0; m < rrProps.size(); m++) {
						if (m > 0) {
							sql += ",',',";
						}
						sql += rrProps.get(m).getName();
					}
					sql += ")";
				} else {
					sql += p.getName();
				}
				sql += " as " + lp.getId();
			}
			sql += " from " + r.getRelatedObjectId() + " where (";
			for (int i = 0; i < rrProps.size(); i++) {
				if (i > 0) {
					sql += ",";
				}
				sql += rrProps.get(i).getName();
			}
			sql += ") in (" + selectId + ")";
			String vals = "";
			for (int i = 0; i < rProps.size(); i++) {
				MProperty p = rProps.get(i);
				MViewProperty lp = form.getViewPropertyByPropertyId(p.getId());
				if (i > 0) {
					vals += ",";
				}
				vals += p.getSqlValue(row.get(lp.getId()));
			}
			String s = String.format(sql, vals);
			List<Map<String, Object>> relatedData = woDao.queryList(s);
			row.putAll(getUIValues(relatedData));
		}
	}

	/**
	 * @param form
	 * @param keyVal
	 */
	public void loadUpdateForm(MViewDto form, String keyVal) {
		if (WoKit.isEmpty(keyVal)) {
			keyVal = form.getKeyValueAdaptor().getKeyValues(form);
			if (WoKit.isEmpty(keyVal)) {
				throw new MException(MException.PK_VALUE, form.getId());
			}
		}
		form.setKeyValuesString(keyVal);
		Map<String,Object> req = form.getApp().getObject().getRequest();
		Set<MRelation> relations = new HashSet<MRelation>();
		StringBuffer sql = new StringBuffer();
		// 1.获取xxx='yyy', aaa=123, ...
		getSqlFieldsForUpdateView(sql, form, relations);
		// 2.获取：from xxx yyy left join aaa bbb on (yyy.ccc = bbb.ddd)
		getSqlFromJoin(sql, form.getApp().getObject(), relations);
		// 3.where (yyy.aaa, yyy.bbb) = ('ccc', 123)
		sql.append(" where ");
		sql.append(form.getSqlPKCondition(true));
		// 4.select
		sql.insert(0, "select ");
		// 获取数据，包括关联M21/M1/1M关系的属性值
		Map<String, Object> data = woDao.queryOne(sql.toString());
		// TODO 获取关联12M/M2M关系的属性值
		this.set2MValues(form, data, relations);
		// 加载除了主键属性之外的视图属性值
		loadForm(form, data);
	}

    /**
     *
     * @param sql
     * @param vw
     * @param relations
     */
	private void getSqlFieldsForUpdateView(StringBuffer sql, MViewDto vw, Set<MRelation> relations) {
		MObjectDto obj = vw.getApp().getObject();
		/**
		 * 1. xxx.xx as yyy , xxx.xx as yyy , xxx.xx as yyy
		 * 对于本MObjectDto的MProperty
		 * ，则xxx为MObjectDto.No，xx为MProperty.W_NAME，yyy为对应lp.Id；
		 * 对于关联的MObjectDto的MProperty
		 * ，则xxx为相应MRelation.Id，xx为MProperty.W_NAME，yyy为对应lp.Id
		 */
		for (MViewProperty vp : vw.getProperties()) {
			MRelation r = vp.getRelation();
			if (WoKit.isEmpty(vp.getPropertyId())) {
				if (r == null) {
					log.warn("视图属性[" + vp.getId() + "]没有配置属性和关系!");
				} else if (r.is12M() || r.isM2M()) {
					relations.add(r);
					r.getData().addToList("woFormProp", vp);
				} else if (r.isM21() || r.isM1() || r.is1M()) {
					List<MProperty> props = r.getProperties();
					sql.append(",CONCAT(");
					for (int i = 0; i < props.size(); i++) {
						MProperty prop = props.get(i);
						if (i > 0) {
							sql.append(",',',");
						}
						sql.append(obj.getNo() + "." + prop.getName());
					}
					sql.append(")");
					sql.append(" as " + vp.getId());
					relations.add(r);
					r.getData().addToList("woFormProp", vp);
				} else if (r.isM2CM() || r.isM2C1()) {
				} else {
					throw new MException(MException.VIEW_PR, vp.getId());
				}
				continue;
			}
			MProperty prop = obj.getProperty(vp.getPropertyId());
			if (prop == null) {
				if (r == null) {
					throw new MException(MException.VIEW_PR, vp.getId());
				}
				prop = r.getRelatedObject().getProperty(vp.getPropertyId());
				if (r.is1M() || r.isM1() || r.isM21()) {
					sql.append("," + r.getId() + "." + prop.getName());
					relations.add(r);
					r.getData().addToList("woFormProp", vp);
				} else {
					throw new MException(MException.VIEW_PR, vp.getId());
				}
			} else {
				sql.append("," + obj.getNo() + "." + prop.getName());
			}
			sql.append(" as " + vp.getId());
		}
		sql.deleteCharAt(0);
	}

	/**
	 * submitUpdateForm时，提交的M21关联对象的属性值，作为update处理
	 * 
	 * @param relations
	 * @param form
	 */
	private void updateRelatedProps(Set<MRelation> relations, MViewDto form) {
		for (MRelation r : relations) {
			if (r.isM21()) {
				List<MViewProperty> fps = (List<MViewProperty>) r.getData().get("woFormProp");
				MViewProperty fpRelation = (MViewProperty) r.getData().get("woFormPropRelation");
				if (fps != null) {
					MObjectDto rObj = r.getRelatedObject();
					if (fpRelation == null) {
						throw new MException(MException.FP_RELATION, rObj.getName());
					}
					String rKeyValues = (String) fpRelation.getValue();
					List<MProperty[]> uniques = r.getRelatedObject().getUniqueProps();
					// 关联对象无唯一键
					if (uniques.size() == 0) {
					} else {
						int contains = setUniquesValue(fps, uniques);
						// fps 包含所有唯一键，则抛异常
						if (contains == 1) {
							// 验证插入时是否会违反唯一性约束
							validateUniques(uniques, rObj, rKeyValues);
						} else if (contains == 0) {
							Map<String, Object> one = woDao.queryOne(rObj.getObjectSQL(rKeyValues));
							this.setUniquesValue(one, uniques);
							// 验证插入时是否会违反唯一性约束
							validateUniques(uniques, rObj, rKeyValues);
						}
					}
					// 更新关联对象表sql
					String sql = "update " + r.getRelatedObjectId() + " set ";
					// 处理表单属性的值
					for (int i = 0; i < fps.size(); i++) {
						MViewProperty fp = fps.get(i);
						if (i > 0) {
							sql += ",";
						}
						MProperty p = fp.getPropertyIncludeRelatedObjects();
						sql += p.getName() + "=" + p.getSqlValue(fp.getValue());
					}
					sql += " where " + rObj.getKeysEqualConditions(false, rKeyValues);
					woDao.update(sql);
				}
			}
		}
	}

	/**
	 * @param form
	 */
	public void submitUpdateForm(MViewDto form) {
		MObjectDto obj = form.getApp().getObject();
		BigMap<String,Object> req = obj.getRequest();
		form.setKeyValuesByRequest(req);
		// fields of update sql: xxx='aaa',yyy=123
		String sqlFields = "";
		String used = "used";
		for (MProperty p : obj.getProperties()) {
			String val = req.getString(p.getId());
			if (WoKit.isEmpty(val)) {
				continue;
			}
			sqlFields += "," + p.getName() + "=" + p.getSqlValue(val.toString());
			MViewProperty fp = form.getViewPropertyByPropertyId(p.getId());
			if (fp != null) {
				p.getData().put("woUsed", used);
				req.put(fp.getId(), val);
				fp.setValue(val);
			}
		}
		Set<MRelation> relatedRelations = new HashSet<MRelation>();
		for (MViewProperty fp : form.getProperties()) {
			if (fp.getSubmit() == 0) {
				continue;
			}
			String val = req.getString(fp.getId());
			if (fp.isReplaceSubmit(val)) {
				val = this.woVelocityKit.parse(fp.getSubmitValue(), req);
				req.put(fp.getId(), val);
			}
			String vals[] = new String[0];
			if (!WoKit.isEmpty(val)) {
				vals = val.split(",");
			}
			fp.setValue(val);
			// TODO 正确性检查
			// 获取关系
			MRelation r = fp.getRelation();
			// 从本对象获取属性
			MProperty p = null;
			if (!WoKit.isEmpty(fp.getPropertyId())) {
				p = obj.getProperty(fp.getPropertyId());
				// 本方属性存在
				if (p != null) {
					if (p.getKey() == 1 || p.getData().get("woUsed") == used
							|| p.getName().equalsIgnoreCase(obj.getNoPropName())) {
						continue;
					}
					sqlFields += "," + p.getName() + "=" + p.getSqlValue(val);
					continue;
				}
				// 本方属性不存在，且关系不存在
				if (r == null) {
					throw new MException(MException.VIEW_PR, fp.getId());
				}
				// 关联方对象属性，且关系为12M或者M2M
				if (r.is12M() || r.isM2M()) {
					throw new MException(MException.VIEW_PR, fp.getId());
				}
				// 获取关联方对象属性
				p = r.getRelatedObject().getProperty(fp.getPropertyId());
				// 关联方对象属性不存在，且属性存在
				if (p == null) {
					throw new MException(MException.VIEW_PR, fp.getId());
				}
				r.getData().addToList("woFormProp", fp);
				relatedRelations.add(r);
				continue;
			}
			// 属性ID为空，且关系对象为空
			if (r == null) {
				log.warn(obj.getId() + "属性和关系同时为空！");
				continue;
			}
			// 属性ID为空，关系只能是12M和M2M
			if (r.is12M()) {
				// TODO
			} else if (r.isM21()) {
				if (!WoKit.isEmpty(req.getString(r.getPropertyIds()))) {
					continue;
				}
				List<MProperty> rProps = r.getProperties();
				if (vals.length == 0) {
					vals = new String[rProps.size()];
				}
				if (vals.length != rProps.size()) {
					throw new MException(MException.VIEW_RV, fp.getId());
				}
				for (int i = 0; i < rProps.size(); i++) {
					MProperty rP = rProps.get(i);
					sqlFields += "," + rP.getName() + "=" + rP.getSqlValue(vals[i]);
				}
				r.getData().put("woFormPropRelation", fp);
				relatedRelations.add(r);
				// 如果是创建视图，并且配置了编号属性，则自动创建编号：上级编号+最大下级编号
				if (obj.hasNoProperty()) {
					MRelation pr = obj.getParentRelation();
					if (pr == null) {
						throw new MException(MException.NO_PARENT, obj.getId());
					}
					if (pr.getId().equals(r.getId()) && "1".equals(req.get(fp.getSelectorHiddenName()))) {
						String noValue = obj.createNextChildNoValue((String) val);
						// 插入编号属性值
						if (!WoKit.isEmpty(noValue)) {
							sqlFields += "," + obj.getNoPropName() + "='" + noValue + "'";
						}
					}
				}
			} else if (r.isM2M() || r.isM2CM()) {
				updateBridgeData(form, fp, vals, false);
			} else {
				throw new MException(MException.VIEW_PR, fp.getId());
			}
		}
		// 验证唯一性
		List<MProperty[]> uniques = obj.getUniqueProps();
		if (uniques.size() > 0) {
			int contains = setUniquesValue(form.getProperties(), uniques);
			// fps 包含所有唯一键，则抛异常
			if (contains == 1) {
				// 验证插入时是否会违反唯一性约束
				validateUniques(uniques, obj, form.getUIKeyValues());
			} else if (contains == 0) {
				Map<String, Object> one = woDao.queryOne(obj.getObjectSQL(form.getUIKeyValues()));
				this.setUniquesValue(one, uniques);
				// 验证插入时是否会违反唯一性约束
				validateUniques(uniques, obj, form.getUIKeyValues());
			}
		}
		// 更新关联对象表数据
		updateRelatedProps(relatedRelations, form);
		// 更新本对象数据
		String sql = String.format("update %s set %s where %s", obj.getId(), sqlFields.substring(1),
				form.getSqlPKCondition(false));
		woDao.update(sql);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see wos.engine.services.IWoAppService#deleteObjects(java.lang.Object,
	 * java.lang.String)
	 */
	@Override
	public void deleteObjects(MObjectDto obj, String keyValues) {
		// 删除自身对象（如果有编号属性，将删除所有下级对象）
		String sql = obj.getDeleteSQL(keyValues);
		woDao.delete(sql);
		// 同时删除多对多关系数据
		List<String> sqlsM2M = obj.getDeleteM2MSQL(keyValues);
		for (String s : sqlsM2M) {
			woDao.delete(s);
		}
		// 如果有12M关联数据，则不允许删除
		Map<MRelation, String> sqls12M = obj.getCount12MSQL(keyValues);
		for (MRelation r : sqls12M.keySet()) {
			Integer count = woDao.queryInteger(sqls12M.get(r));
			if (count > 0) {
				throw new MException(MException.R_12M_DATA, r.getRelatedObject().getName());
			}
		}
	}
}
